#include "time.h"

#include <time.h>

#include <QDateTime>

#include "../log/easylogging++.h"

namespace qb {
namespace base {
namespace {
void WordToString(unsigned short w, int len, char* str) {
  for (int i = len - 1; i >= 0; i--) {
    char d = (char)(w % 10);
    str[i] = '0' + d;
    w = w / 10;
  }
}
}  // namespace
class HighResTimeSupport {
 public:
  static HighResTimeSupport& Get() {
    static HighResTimeSupport hrts;
    return hrts;
  }
  HighResTimeSupport() {}
  inline uint64_t GetTime() { return el::base::utils::DateTime::now(); }
};
void HighResTime::ToLocalTime(SYSTEMTIME& stLocal) const {
  QDateTime dt = QDateTime::fromMSecsSinceEpoch(m_time / 1000);
  stLocal.wYear = dt.date().year();
  stLocal.wMonth = dt.date().month();
  stLocal.wDay = dt.date().day();
  stLocal.wDayOfWeek = dt.date().dayOfWeek();
  stLocal.wHour = dt.time().hour();
  stLocal.wMinute = dt.time().minute();
  stLocal.wSecond = dt.time().second();
  stLocal.wMilliseconds = dt.time().msec();
}
HighResTime HighResTime::today() {
  return HighResTime(m_time) - todaypassed();  // 0:0:0?
}
HighResTime HighResTime::todaypassed() {
  SYSTEMTIME time;
  ToLocalTime(time);
  double start =
      ((time.wHour * 3600 + time.wMinute * 60 + time.wSecond) * 1000 +
       time.wMilliseconds) *
      1000.0;
  return HighResTime(start);
}
BaseString HighResTime::ToString(const char* format) const {
  // YMD
  // hmsi
  const char* fmt = format ? format : "Y/M/D h:m:s-i";
  BaseString timestr(64);
  int len = strlen(fmt);
  SYSTEMTIME st;
  ToLocalTime(st);
  char buf[16] = {0};
  for (int i = 0; i < len; i++) {
    switch (fmt[i]) {
      case 'Y':
        WordToString(st.wYear, 4, buf);
        timestr.append(buf, 4);
        break;
      case 'M':
        WordToString(st.wMonth, 2, buf);
        timestr.append(buf, 2);
        break;
      case 'D':
        WordToString(st.wDay, 2, buf);
        timestr.append(buf, 2);
        break;
      case 'h':
        WordToString(st.wHour, 2, buf);
        timestr.append(buf, 2);
        break;
      case 'm':
        WordToString(st.wMinute, 2, buf);
        timestr.append(buf, 2);
        break;
      case 's':
        WordToString(st.wSecond, 2, buf);
        timestr.append(buf, 2);
        break;
      case 'i':
        WordToString(st.wMilliseconds, 3, buf);
        timestr.append(buf, 3);
        break;
      default:
        timestr.push_back(fmt[i]);
        break;
    }
  }
  return timestr;
}
HighResTime::HighResTime() : m_time(HighResTimeSupport::Get().GetTime()) {}
uint64_t GetHighResTimeD() {
  return HighResTimeSupport::Get().GetTime();  // 微秒
}
unsigned int GetHighResTime() {
  return (unsigned int)(HighResTimeSupport::Get().GetTime() / 1000);  // 毫秒
}

struct tm time2tm(time_t t) {
  tm tmResult;
#ifdef _MSC_VER
  localtime_s(&tmResult, &t);
#else
  localtime_r(&t, &tmResult);
#endif
  return tmResult;
}

}  // namespace base
}  // namespace qb

#ifndef _MSC_VER
unsigned long GetTickCount() {
  struct timespec ts;
  clock_gettime(CLOCK_MONOTONIC, &ts);
  return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
}
#endif